package com.cloud.mkz.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.mkz.admin.api.dto.AuthedUser;
import com.cloud.mkz.admin.api.dto.RoleMenuDto;
import com.cloud.mkz.admin.api.dto.UserBean;
import com.cloud.mkz.admin.api.entity.RbacMenu;
import com.cloud.mkz.admin.api.entity.RbacUser;
import com.cloud.mkz.admin.api.entity.RbacUserRole;
import com.cloud.mkz.admin.mapper.RbacUserMapper;
import com.cloud.mkz.admin.mapper.RbacUserRoleMapper;
import com.cloud.mkz.admin.service.RbacUserService;
import com.cloud.mkz.core.common.constant.CacheConstants;
import com.cloud.mkz.core.common.util.ResponseResult;
import com.cloud.mkz.core.common.util.UtilTools;
import com.cloud.mkz.core.common.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 系统用户 接口实现类
 *
 * @author mkz code generator
 * @date 2021-03-19 16:15:25
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RbacUserServiceImpl extends ServiceImpl<RbacUserMapper, RbacUser> implements RbacUserService {
    private final RedisUtil redisUtil;
    private final RbacUserRoleMapper rbacUserRoleMapper;

    @Override
    public ResponseResult<UserBean> login(String randomStr, RbacUser rbacUser, List<RbacUserRole> userRoles){
        String tokenStr = UtilTools.getTokenStr();
        Timestamp loginTime = new Timestamp(System.currentTimeMillis());
        rbacUser.setLastLoginTime(loginTime);
        this.baseMapper.updateById(rbacUser);
        UserBean result = new UserBean(rbacUser, tokenStr);
        Map<String, List<String>> permissions = getMyPermissions(userRoles);
        result.setPermissions(permissions);
        List<RbacMenu> menuTree = getMyTree(userRoles);
        if(!dealCacheInfo(randomStr, rbacUser, result, permissions, menuTree)){
            return ResponseResult.failed(5, "系统缓存更新错误");
        }
        return ResponseResult.ok(result, "登录成功");
    }

    /**
     * 处理登录缓存信息
     * @param randomStr 图片验证码随机数Key
     * @param rbacUser 登录用户实体信息
     * @param userBean 登录成功返回实体
     * @param permissions 登录用户的权限
     * @param menuTree 登录用户菜单树
     * @return boolean
     */
    private boolean dealCacheInfo(String randomStr, RbacUser rbacUser, UserBean userBean,
                                  Map<String, List<String>> permissions, List<RbacMenu> menuTree){
        boolean result = true;
        AuthedUser authedUser = new AuthedUser(rbacUser);
        authedUser.setAuthToken(userBean.getAuthToken()).setLoginTime(rbacUser.getLastLoginTime())
                .setPermissions(permissions).setMenuTree(menuTree);
        boolean cacheStatus = redisUtil.set(CacheConstants.PROJECT_OAUTH_TOKEN + userBean.getAuthToken(), authedUser, 2, TimeUnit.HOURS);
        if(!cacheStatus){return false;}
        cacheStatus = redisUtil.lSet(CacheConstants.SYSTEM_ONLINE_DETAILS, authedUser);
        if(!cacheStatus){return false;}
        redisUtil.del(CacheConstants.DEFAULT_CODE_KEY + randomStr);
        return result;
    }

    /**
     * 获取当前用户角色的权限（菜单和按钮）信息
     * @param userRoles
     * @return Map<String, List<String>>
     */
    private Map<String, List<String>> getMyPermissions(List<RbacUserRole> userRoles){
        List<String> myMenus = new ArrayList<>(16);
        List<String> myAuths = new ArrayList<>(16);
        Map<String, List<String>> result = new HashMap<>(16);
        Map<String, List<RoleMenuDto>> roleMenuMap = (Map<String, List<RoleMenuDto>>)redisUtil.get(CacheConstants.SYSTEM_ROLE_MENU_AUTH_DATA);
        if(CollectionUtil.isEmpty(roleMenuMap)){
            result.put(CacheConstants.USER_PERMISSION_MENU_DETAILS, myMenus);
            result.put(CacheConstants.USER_PERMISSION_AUTH_DETAILS, myAuths);
            return result;
        }
        List<RoleMenuDto> tempRoleMenuList = null;
        for(RbacUserRole userRole : userRoles) {
            tempRoleMenuList = roleMenuMap.get(userRole.getRoleId().toString());
            if(CollectionUtil.isEmpty(tempRoleMenuList)){
                continue;
            }
            for(RoleMenuDto roleMenuDto : tempRoleMenuList) {
                if(roleMenuDto.getMenuType()==2){
                    if(!myMenus.contains(roleMenuDto.getMenuUrl())){
                        myMenus.add(roleMenuDto.getMenuUrl());
                    }
                }
                if(roleMenuDto.getMenuType()==3){
                    if(!myAuths.contains(roleMenuDto.getAuthorityKey())){
                        myAuths.add(roleMenuDto.getAuthorityKey());
                    }
                }
            }
        }
        result.put(CacheConstants.USER_PERMISSION_MENU_DETAILS, myMenus);
        result.put(CacheConstants.USER_PERMISSION_AUTH_DETAILS, myAuths);
        return result;
    }

    /**
     * 获取当前用户的菜单树信息
     * @param userRoles
     * @return List<RbacMenu>
     */
    private List<RbacMenu> getMyTree(List<RbacUserRole> userRoles){
        List<RbacMenu> result = new ArrayList<>();
        List<Long> havedIds = new ArrayList<>();
        Map<String, List<RoleMenuDto>> roleMenuMap = (Map<String, List<RoleMenuDto>>)redisUtil.get(CacheConstants.SYSTEM_ROLE_MENU_AUTH_DATA);
        if(CollectionUtil.isEmpty(roleMenuMap)){
            return result;
        }
        List<RoleMenuDto> tempRoleMenuList = null;
        for(RbacUserRole userRole : userRoles) {
            tempRoleMenuList = roleMenuMap.get(userRole.getRoleId().toString());
            if(CollectionUtil.isEmpty(tempRoleMenuList)){
                continue;
            }
            for(RoleMenuDto roleMenuDto : tempRoleMenuList) {
                if(roleMenuDto.getMenuType()==1||roleMenuDto.getMenuType()==2){
                    if(!havedIds.contains(roleMenuDto.getMenuId())){
                        result.add(new RbacMenu(roleMenuDto));
                        havedIds.add(roleMenuDto.getMenuId());
                    }
                }
            }
        }
        return result;
    }
}
